# 09 - UI 渲染系统

## 概述

Claude Code 的终端 UI 使用 **React** 构建，通过一个**自定义的 Ink 渲染器**（不是 npm 上的 Ink 库）将 React 组件渲染到终端。

---

## 渲染管道

```
React 组件树
  │
  ▼
React Reconciler (自定义)
  │ — 将 React 元素转为虚拟 DOM 节点
  ▼
Virtual DOM (DOMElement)
  │
  ▼
Yoga WASM (Flexbox 布局)
  │ — 计算每个节点的 x, y, width, height
  ▼
Output (cell grid)
  │ — 将节点渲染为字符单元格矩阵
  ▼
Diff & Patch
  │ — 与上一帧对比，只更新变化的部分
  ▼
ANSI 转义序列
  │
  ▼
终端显示
```

---

## 关键文件

| 文件 | 大小 | 作用 |
|------|------|------|
| `ink/ink.tsx` | ~251KB | 核心渲染引擎 |
| `ink/render-node-to-output.ts` | | DOM 节点 → 输出 |
| `ink/screen.ts` | | 屏幕管理 |
| `ink/reconciler.ts` | | React Reconciler 实现 |
| `ink/dom.ts` | | 虚拟 DOM 操作 |
| `ink/output.ts` | | 输出缓冲区管理 |

---

## 自定义 React Reconciler

Claude Code 实现了自己的 React Reconciler（类似 react-dom 对浏览器 DOM 的角色）：

```typescript
// 简化概念
const reconciler = ReactReconciler({
  createInstance(type, props) {
    // 创建终端 DOM 节点
    return new DOMElement(type)
  },
  appendChildToContainer(container, child) {
    // 将子节点添加到容器
    container.appendChild(child)
  },
  commitUpdate(instance, oldProps, newProps) {
    // 更新节点属性
    instance.update(newProps)
  },
  // ... 更多 host config 方法
})
```

---

## 布局引擎：Yoga WASM

使用 Facebook 的 **Yoga** 布局引擎（编译为 WASM）实现 **Flexbox** 布局：

```
<Box flexDirection="column" padding={1}>
  <Box flexDirection="row">
    <Text>左侧</Text>
    <Box flexGrow={1} />
    <Text>右侧</Text>
  </Box>
  <Text>底部内容</Text>
</Box>
```

Yoga 计算出每个元素在终端中的精确位置（行、列、宽度、高度）。

---

## 组件体系 (`components/`)

### 核心组件

146+ 组件，组织在 `src/components/` 下：

| 类别 | 代表组件 | 说明 |
|------|----------|------|
| **布局** | `Box`, `Spacer` | Flexbox 容器 |
| **文本** | `Text`, `Markdown` | 文本渲染与 Markdown |
| **输入** | `TextInput`, `MultilineTextInput` | 文本输入框 |
| **反馈** | `Spinner`, `ProgressBar` | 加载动画、进度条 |
| **对话** | `Dialog`, `PermissionDialog` | 对话框 |
| **消息** | `MessageView`, `ToolUseView` | 对话消息显示 |
| **导航** | `SelectList`, `TabBar` | 选择列表、标签页 |

### 消息渲染

每条对话消息的渲染由 Tool 自己控制：

```typescript
// Tool 接口中的渲染方法
renderToolUseMessage(input, options): React.ReactNode    // 调用显示
renderToolResultMessage(output, progress, options): React.ReactNode  // 结果显示
```

这意味着每个 Tool 决定自己在终端中如何展示（是展开显示文件内容，还是折叠显示摘要等）。

---

## Hooks (`hooks/`)

87 个 React Hooks，管理 UI 状态和行为：

| Hook | 作用 |
|------|------|
| `useCanUseTool` | 判断 Tool 是否可执行 |
| `useTheme` | 主题管理 |
| `useInput` | 键盘输入处理 |
| `useTerminalSize` | 终端尺寸监听 |
| `useFocus` | 焦点管理 |
| `useMessages` | 消息列表管理 |
| `useScroll` | 滚动控制 |

---

## 渲染优化

### 增量更新

不是每帧都重绘整个终端，而是：

```
当前帧 Output  vs  上一帧 Output
        │                │
        └──→ Diff ──→ 只重绘变化的区域
```

### Cell Grid

终端被抽象为一个二维的字符单元格矩阵：

```
每个 Cell = {
  char: string     // 字符
  fg: Color        // 前景色
  bg: Color        // 背景色
  bold: boolean    // 粗体
  // ... 其他样式
}
```

### 性能目标

```
目标帧率: 根据内容复杂度动态调整
渲染去抖: 短时间内多次状态更新合并为一次渲染
```

---

## Vim 模式 (`vim/`)

完整的 Vim 键绑定支持：

- Normal / Insert / Visual 模式
- 常用 motion (hjkl, w, b, e, 0, $)
- 操作符 (d, c, y)
- 文本对象 (iw, aw, i", a")

通过 `/vim` 命令切换开关。

---

## 快捷键系统 (`keybindings/`)

支持用户自定义快捷键：

```json
// ~/.claude/keybindings.json
[
  {
    "key": "ctrl+s",
    "command": "submit"
  },
  {
    "key": "ctrl+k ctrl+c",
    "command": "compact"
  }
]
```

支持 chord 绑定（组合键序列，如 Ctrl+K 然后 Ctrl+C）。
